package com.dragonwu.utils;

import org.springframework.stereotype.Component;

/**
 * 采用twitter的雪花算法，生成有一定顺序且不重复的id，结果类型为64位的long型
 */
@Component
public class SnowflakeIdGen {
    //集群id
    private final long dataCenterId;
    //机器id
    private final long workerId;
    //集群id的bit位数
    private final long dataCenterIdBits = 5L;
    //机器id的bit位数
    private final long workerIdBits = 5L;
    //序列号
    private long sequenceId;
    //上一次生成id使用的timestamp ，以毫秒为单位
    private long lastTimestamp = 1L;

    /**
     * 若没有指定集群id和机器id,则默认均为0
     */
    public SnowflakeIdGen() {
        this(0, 0);
    }

    /**
     * 指定集群id和机器id
     */
    public SnowflakeIdGen(long dataCenterId, long workerId) {
        //集群id的最大编号
        long maxdataCenterId = ~(-1L << dataCenterIdBits);
        if (dataCenterId < 0 || dataCenterId > maxdataCenterId) {
            throw new RuntimeException(String.format("dataCenterId greater than %d or less than 0", maxdataCenterId));
        }
        //机器id的最大编号
        long maxWorkerId = ~(-1L << workerIdBits);
        if (workerId < 0 || workerId > maxWorkerId) {
            throw new RuntimeException(String.format("workerId greater than %d or less than 0", maxWorkerId));
        }
        this.dataCenterId = dataCenterId;
        this.workerId = workerId;
    }

    /**
     * 生成全局唯一的id
     */
    public synchronized long nextId() {
        long timestamp = System.currentTimeMillis();
        if (timestamp < lastTimestamp) {  //出现这种情况，通常是由于机器时间出问题了
            throw new RuntimeException("machine time error");
        }

        //同一时刻生成的id号
        //序列号的bit位数
        long sequenceIdBits = 12L;
        if (timestamp == lastTimestamp) {
            //序列号的掩码
            long sequenceIdMask = ~(-1L << sequenceIdBits);
            sequenceId = (sequenceId + 1) & sequenceIdMask;
            if (sequenceId == 0) {  //说明当前毫秒的序列号用完了，需从下个毫秒数开始重新计数
                timestamp = nextTimestamp(lastTimestamp);
            }
        } else {
            //否则序列号从0开始
            sequenceId = 0L;
        }

        lastTimestamp = timestamp;
        //生成最终结果时，集群id需移动的bit位数
        long timestampShiftBits = sequenceIdBits + workerIdBits + dataCenterIdBits;
        //生成最终结果时，集群id需移动的bit位数
        long dataCenterIdShiftBits = sequenceIdBits + workerIdBits;
        //生成最终结果时，机器id需移动的bit位数
        //去掉过去的时间，即从指定时间(本例以2017-10-12 00:00:00)开始算，
        // 大约可用69.5年（41位的时间位，最大值换成毫秒，再换算成年，大约69.5年）
        //1507737600000为从1970-01-01 00:00:00到2017-10-12 00:00:00经过的毫秒数
        long pastMills = 1507737600000L;
        return ((timestamp - pastMills) << timestampShiftBits)
                | (dataCenterId << dataCenterIdShiftBits)
                | (workerId << sequenceIdBits)
                | sequenceId;
    }

    /**
     * 获取上次取数毫秒的下一时刻
     */
    long nextTimestamp(long lastTimestamp) {
        long timestamp = System.currentTimeMillis();
        while (timestamp <= lastTimestamp) {
            timestamp = System.currentTimeMillis();
        }
        return timestamp;
    }
}
